﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using A2DFramework.AOPService.Core;
using A2DFramework.AOPService;
using System.Runtime.Remoting.Messaging;
using System.Reflection;
using A2DFramework.CacheService.CacheAttributes;
using System.Diagnostics;

namespace A2DFramework.CacheService.ConcreteAOPProcessor
{
    class CachableAOPProcessor : IAOPProcessor
    {
        private static List<Type> valueTypes = new List<Type>() { 
                                                                    typeof(string),
                                                                    typeof(int),
                                                                    typeof(Guid),
                                                                    typeof(float),
                                                                    typeof(double),
                                                                    typeof(bool)
                                                                };

        public AOPService.FollowAction PreProcess(System.Runtime.Remoting.Messaging.IMessage requestMsg)
        {
            bool cachableTagExists = true;
            bool cacheEvictTagExists = true;
            bool nocachableTagExists = true;
            CachableAttribute cachableAttr = AOPHelper.CheckDefinationTag<CachableAttribute>(requestMsg, ref cachableTagExists);
            CacheEvictAttribute cacheEvictAttr = AOPHelper.CheckDefinationTag<CacheEvictAttribute>(requestMsg, ref cacheEvictTagExists);
            NoCachableAttribute nocachableAttr = AOPHelper.CheckDefinationTag<NoCachableAttribute>(requestMsg, ref nocachableTagExists);

            if (nocachableTagExists)
                return new FollowAction(true);
            if (!cachableTagExists)
                return new FollowAction(true);

            string cacheKey = AssembleCacheKey(requestMsg, cachableAttr.Key);

            object o = CacheManager.Instance().GetCacheCore(cachableAttr.Store).Get(cacheKey);
            if (o == null)
                return new FollowAction(true);

            FollowAction follow = new FollowAction(false);//stop all follow actions
            follow.Result = o;
            return follow;
        }

        
        public void PostProcess(System.Runtime.Remoting.Messaging.IMessage requestMsg, System.Runtime.Remoting.Messaging.IMessage responseMsg)
        {
            bool cachableTagExists = true;
            bool cacheEvictTagExists = true;
            bool nocachableTagExists = true;
            CachableAttribute cachableAttr = AOPHelper.CheckDefinationTag<CachableAttribute>(requestMsg, ref cachableTagExists);
            CacheEvictAttribute cacheEvictAttr = AOPHelper.CheckDefinationTag<CacheEvictAttribute>(requestMsg, ref cacheEvictTagExists);
            NoCachableAttribute nocachableAttr = AOPHelper.CheckDefinationTag<NoCachableAttribute>(requestMsg, ref nocachableTagExists);

            if (nocachableTagExists)
                return;

            if (cachableTagExists)
            {
                ReturnMessage returnMsg = (ReturnMessage)responseMsg;

                string cacheKey = AssembleCacheKey(requestMsg, cachableAttr.Key);

                //CacheManager.Instance().GetCacheCore(cachableAttr.Store).Set(cacheKey, returnMsg.ReturnValue);
                CacheManager.Instance().GetCacheCore(cachableAttr.Store).Set(cacheKey, returnMsg.ReturnValue, cachableAttr.ExpireType, cachableAttr.Increment);
            }
            if (cacheEvictTagExists)
            {
                string cacheKey = AssembleCacheKey(requestMsg, cacheEvictAttr.Key);
                CacheManager.Instance().GetCacheCore(cacheEvictAttr.Store).Remove(cacheKey);
            }
        }

        private static string AssembleCacheKey(System.Runtime.Remoting.Messaging.IMessage requestMsg, string cacheKey)
        {
            IMethodCallMessage msg = (IMethodCallMessage)requestMsg;
            for (int i = 0; i < msg.InArgCount; i++)
            {
                string argName = msg.GetInArgName(i);
                object argObj = msg.GetInArg(i);
                Type argObjType = argObj.GetType();

                if (valueTypes.Contains(argObjType))
                {
                    cacheKey = cacheKey.Replace("{" + argName + "}", argObj.ToString());
                    continue;
                }
                PropertyInfo[] argObjProperties = argObj.GetType().GetProperties(/*System.Reflection.BindingFlags.Public*/);
                foreach (PropertyInfo pi in argObjProperties)
                {
                    object piValue = pi.GetValue(argObj, null);
                    cacheKey = cacheKey.Replace("{" + argName + "." + pi.Name + "}", piValue.ToString());
                }
            }
            return cacheKey;
        }
    }
}
